跳到主要内容

01-04 深入浅出处理器

01 深入浅出处理器(上)

处理器:微控制器VS微处理器

  • 微控制器:CPU+片内内存+片内外设
  • 微处理器:CPU

注意:微控制器具有成本低,功耗低等优点,常用于嵌入式系统设计;对于软件工程师而言,微控制器和微处理器没有任何区别。

寄存器的分类

  • CPU寄存器:专用指令执行,数据运算,变量处理,参数传递

  • 外设寄存器:用于控制外设的行为和方式;寄存器值的配置需要根据芯片手册完成

处理器中的关键寄存器

  • PC-程序计数器(指令指针IP):每执行一条指令,PC的值就会发生变化;PC始终保存下一条CPU要执行的指令地址。
  • SP-栈指针(Stack Pointer):始终指向栈空间的顶端,实现LIFO特性;保存中断断点,保存函数调用的返回点,保存CPU现场数据,等

PC和SP的使用案例:函数调用

处理器的IO操作

  • 处理器与外设之间的数据通信通过IO操作完成
  • 内存映射IO空间:外设通过精密的硬件连接映射到处理器的地址空间;通过地址访问的方式与外设进行通信
  • 独立IO空间:独立于地址空间,无法通过地址访问外设;使用专用指令与外设进行通信

操作外设的基本方式:通过IO端口配置寄存器;通过IO端口读取数据寄存器。从软件工程师的角度来看,只需要对不同的IO端口读写就可以操作外设;读取IO端口时候的具体和意义需要查询具体的硬件手册。

硬件设计的关键:通过具体的硬件设计,可以使得地址总线上的值在某个范围时,片选信号为“真”,即:使能连接的设备,从而读取相应地址中的数据!

设备地址映射示例

处理器启动过程简介:处理器上电后,PC寄存器固化了一个默认值;PC默认值用于决定第一条执行命令;第一条执行指定隶属于启动程序。

处理器启动过程简介

启动程序(Bootloader)

  • 系统上电后运行的第一个程序(Not OS)

  • 根据运行阶段,体积和功能的不同分为三个部分

    • BL0-固化于硬件中,用于初始化硬件,加载并运行BL1
    • BL1-存储于外部存储设备中,用于初始化主存,加载并运行BL2
    • BL2-存储于外部存储设备中,用于引导操作系统执行
  • 实例分析:S3C6410启动过程

02 深入浅出处理器(中)

中断的概念和意义:中断是一种处理器与外设进行通信的机制;用于"通知"处理器外部有"重要事件"发生;一般情况下,中断需要被处理器响应。

中断服务程序(ISR)

  1. 从外设中读取中断状态寄存器的值,以便了解中断类型

  2. 根据中断类型具体设计处理逻辑

  3. 清除外设状态寄存器中的中断标识位

  4. 清除处理器中的中断标识位

软件工程师眼中的中断服务程序:不能有返回值,不能有参数传递;必须短小而高效,避免浮点运算。

//错误示例
__interrupt double compute_area(double radius) {
double area = PI*radius*radius;
printf("\nArea = %f",area);
return area;
}

中断的意义:应用程序不必关心中断的发生与处理;中断服务程序不必关心应用程序的执行状态;中断是"上层应用"与"底层代码"的"分割边界"。

中断的类型

  • 硬中断:通过处理器中断信号线产生的中断
  • 软中断:通过非法指令或特殊指令触发的中断

中断优先级:多个中断同时出现时,处理器先响应高优先级的中断;低优先级中断的ISR执行时,可以被高优先级中断再次打断;ISR比App Code拥有更高的执行优先权。

中断的应用:程序断点

  • 断点指的是调试工具用于暂停代码执行的指令位置

  • 断点的实现原理为处理器的中断支持

    • 软件断点:利用非法指令异常产生中断实现
    • 硬件中断:利用中断寄存器的特性产生中断

程序断点的实现原理

  1. 获取原程序指定行对应的代码地址

  2. 把代码地址中的指令替换为中断触发指令

  3. 在中断服务程序中将控制权交给调试程序

  4. 调试程序读取原程序上下文信息

  5. 调试程序将代码地址中的指令还原

  6. 原程序从断点处继续向下执行

一个工程产品案例的剖析

  • 背景:嵌入式实时系统对时序的要求比较严格;各个线程的执行有相对严格的时间要求。
  • 痛点:断点调试在嵌入式实时系统中不适用

常规解决方案:日志调试法。在代码中的"关键位置"添加打印语句,打印语句尽可能详细的打印上下文信息(函数名,局部变量,等)。当系统出现问题时,查看日志文件,分析问题。

日志调试法存在的问题

  • 不易维护:打印语句分散于产品代码的各个角落
  • 影响效率:过多的打印语句意味着过多的IO操作,影响产品的整体执行效率
  • 分析困难:当日志输出量非常多的时候,很难精确定位问题;也许只有添加打印语句的工程师看得懂日志输出。

一个疯狂的想法:同时结合日志调试法和断点调试法的优点,使得实时系统调试时,能够任意查看指定代码行上下文的信息;并且,不增加打印语句,不暂停执行。

解决方案

  1. 获取原程序指定行对应的代码地址
  2. 把代码地址中的指令替换为中断触发指令
  3. 在中断服务程序中抓取全局信息和栈信息
  4. 抓取的信息发送回调试程序解析并输出

实践结果-MProbe(旗舰产品):基于ARM+Linux平台完整实现;通过中断原理成功获取上下文信息;完全不影响程序的执行效率

  • 产品关键技术点:中断,ISR,编译信息,GDB;GUI,Socket,多线程。

03 深入浅出处理器(下)

内存管理单元(MMU)

  • 现代处理器中对内存进行高效管理的功能单元
  • 操作系统利用内存管理单元能够实现:虚拟内存;内存保护。

有意思的问题:下面的程序,两次运行的输出是否完全相同?为什么?

int g_v = 1;
int main(int argc,char* argv[])
{
printf("g_v = %d\n",g_v);
printf("&g_v = %d\n",&g_v);
system("pause");
return 0;
}

进程1: &g_v = ?

进程2: &g_v = ?

理论上,不同进程在内存中的不同位置执行;因此,全局变量的地址不同。

经常被遗忘的事实:应用程序开发时,面对的内存为虚拟内存;虚拟内存模式下使用的内存地址为虚拟地址;每一个进程拥有独立私有的虚拟地址空间。虚拟内存与实际物理内存无关,是一个假象的足够大的内存。

思考:内存总需求量为1G的应用程序是否能运行于硬件内存为256M的计算机?

虚拟内存的意义:虚拟内存能够支持多个大内存需求量的进程同时运行于较小的物理内存中。 M(P1) + M(P2) > Physical

虚拟内存的机制:虚拟内存需要重新映射到物理内存;虚拟地址映射到物理内存中的实地址;每次只有进程的少量代码在物理内存中运行;大部分进程代码位于存储器中。

页式内存管理:页是内存单位,指一定数量的内存(如:4K);虚拟内存和物理内存以页为单位管理;进程的活动页被载入内存时,记录页地址的映射关系

页式管理法将内存分为两个部分:(p,d)

  • p-地址高位,页面号
  • d-地址低位,页面偏移量

页式内存管理

思考一下:以下几种方式为什么能够提高电脑性能?

  • 更换主频更高的处理器(同系列)
  • 增加物理内存大小
  • 更换SSD固态硬盘

04 深入浅出处理器(续)

一个工艺上的问题:处理器和内存所使用的半导体器件不同;工艺的差异导致了处理器与内存的速度差异;数据处理时,处理器总是需要等待内存。

程序访问的局部性:在短时间内,处理器访问的存储空间是一个很小的范围

  • 时间局部性:某个存储单元在短时间内很可能被再次访问
  • 空间局部性:某个存储单元的邻近单元在短时间内也被访问

高速缓冲存储器的引入(cache):Cache是一种小容量高速存储器;Cache的存取速度与处理器的运算速度几乎同量级;Cache在现代计算机系统中直接内置于处理器芯片中。

Cache解决方案:在处理器和内存之间设置cache;把内存中被频繁访问的指令和数据复制到cache中;大多数情况下,处理器能直接从cache中取得指令和数据。

处理器的数据访问

问题:内存和cache之间如何映射?

直接映射法:将cache和内存分成固定大小的块(如:512Byte/块);内存中的每一块在cache中有固定的映射位置;映射公式为:

  • Poscache=内存块号%cache总块数

特点:任意一个内存地址都能映射到cache中的一个固定位置。

地址划分

映射原理:根据访问地址的中间c位找到cache中的对应块;比较地址的高t位是否和flag相同。

  • 相同:直接读取数据
  • 不相同:从内存中复制块内容

例:当前处理器需要访问内存地址0x0240CH

直接映射法的特点

  • 优点:映射过程简单,所需耗时短
  • 缺点:当短时间内访问的地址有同余冲突时,会造成缓存失效

cache原理的软件应用:项目背景,日志调试工具(Log Dog)

  • 解决的问题:日志对系统效率影响巨大,且不容易分析查看;现有的日志系统无法高效的打印二进制数据;自定义日志内容的解析方式;对日志进行分类,并控制日志是否输出。

  • 出现过的性能问题:当短时间内有大量日志需要打印时,性能无法满足调试需求

  • 最终解决方案:根据cache原理,设置二级缓存机制,尽量避开查找!